iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 21
0
Software Development

用Canvas打造自己的遊樂場系列 第 21

[Day21]用Canvas打造自己的遊樂場-Galaxian 收尾

  • 分享至 

  • xImage
  •  

今天就快速的來幫這遊戲收尾吧,昨天完成的進度到敵人會發射子彈.
那今天要做的事情有兩個,一個是判定玩家被擊中.另一個則是若生命沒了重置遊戲,還有敵人被全滅要重置遊戲.

那就先來寫個重置遊戲

resetGame = () => {
    resetEnemy();
    chance = 3;
}

先把他加入之前寫的玩家擊中敵人的判定式

// 是否擊中
shootCheck = (x, y) => {
    var col = Math.floor ((x - enemy_distance + ENEMY_SPACING_X ) / (ENEMY_R + ENEMY_SPACING_X));
    var row = Math.floor (y / (ENEMY_R + ENEMY_SPACING_Y));

    var enemyIndex = enemyToIndex(col, row);

    if(enemy_grid[enemyIndex] == 1 && col < ENEMY_COLS && col >= 0) {
        console.log(x, y, col, row, enemy_distance, enemyIndex);
        enemy_grid[enemyIndex] = 0;
        player_shot = false;
        enemy_left -= 1;
        if(enemy_left == 0) {
            resetGame();
        }
    }
}

再來就寫個玩家被擊中的判定式

// 敵人是否擊中
enemyShootCheck = () => {
    var distance = Math.sqrt(Math.pow(player_x - enemy_bullet_x, 2) + Math.pow((canvas.height - PLAYER_DIST_FROM_EDGE) - enemy_bullet_y, 2));
    if(distance < PLAYER_R) {
        chance -= 1;
        enemy_shot = false;

        if(chance < 0) {
            resetGame();
        }
    }
}

把放入移動子彈的函式中,不斷做檢查

// 移動子彈
moveBullet = () => {
    // 玩家發射子彈
    if(player_shot) {
        bullet_y -= BULLET_SPEED;

        //確認子但是否擊中
        shootCheck(bullet_x, bullet_y);

        // 若子彈超出邊界,重新上膛
        if(bullet_y < 0) {
            player_shot = false;
        }
    }

    // 敵人發射子彈
    if(enemy_shot) {
        enemy_bullet_y += BULLET_SPEED;

        enemyShootCheck();
        // 若子彈超出邊界,重新上膛
        if(enemy_bullet_y > canvas.height) {
            enemy_shot = false;
        }
    }
}

OKOK!! 第二個遊戲也算是完成啦. 剩下9天了~~~ 這挑戰有點虐人啊

最後附上第二個遊戲的完整程式碼

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Second Game</title>
    <meta name="description" content="第一個遊戲">
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">
</head>

<body>
    <canvas id="playground" width="800" height="630"></canvas>
    <script>

        // 生命設定
        const LIFE_SIZE = 30;
        var chance = 3;

        // 玩家設定
        const PLAYER_DIST_FROM_EDGE = 50; 
        const PLAYER_R = 15;
        var player_x = 400;

        // 玩家子彈設定
        const BULLET_R = 2.5;
        const BULLET_SPEED = 10;
        var bullet_x;
        var bullet_y;
        var player_shot = false;

        // 敵人子彈設定
        var enemy_bullet_x;
        var enemy_bullet_y;
        var enemy_shot = false;

        // 敵人設定
        const ENEMY_R = 20;
        const ENEMY_SPACING_X = 30;
        const ENEMY_SPACING_Y = 25;
        const ENEMY_COLS = 9;
        const ENEMY_ROWS = 7;
        var enemy_grid = new Array(ENEMY_COLS * ENEMY_ROWS);
        var enemy_left = 0;    

        // 敵人移動設定
        var enemy_speed = 3;
        var enemy_distance = 3 * ENEMY_R;
    
        window.onload = () => {
            canvas = document.getElementById('playground');
            canvasContext = canvas.getContext('2d');

            //一秒更新幾次畫面
            var timesPerSec = 30;
            setInterval(drawAll, 1000 / timesPerSec);

            // 抓滑鼠位置
            canvas.addEventListener('mousemove', mousePos);

            // 滑鼠點擊
            canvas.addEventListener('mousedown', mouseClick);
        
            // 重置敵人
            resetEnemy();
        }

        // 負責更新畫面
        drawAll = () => {
            move();
            draw();
        }

        // 負責畫畫
        draw = () => {
            // background
            drawRectangle(0, 0, canvas.width, canvas.height, 'black');

            // 畫線
            drawLine(0, LIFE_SIZE, canvas.width, LIFE_SIZE, 'white')

            // 畫出生命
            showLife(chance);

            // 畫出玩家
            drawCircle(player_x, (canvas.height - PLAYER_DIST_FROM_EDGE), PLAYER_R, 'gray')

            // 顯示子彈
            showBullet();

            // 顯示敵人
            showEnemy();

        }

        // 負責處理動作
        move = () => {
            
            // 子彈移動
            moveBullet();

            // 移動敵人
            moveEnemy();

            // 敵人發射
            enemyShoot();
        }
        
        // 顯示子彈
        showBullet = () => {
            // 如果玩家發射子彈
            if(player_shot) {
                drawCircle(bullet_x, bullet_y, BULLET_R, '#FF9D6F')
            }

            // 如果敵人發射子彈
            if(enemy_shot) {
                drawCircle(enemy_bullet_x, enemy_bullet_y, BULLET_R, '#FF60AF')
            }
        }

        // 玩家發射子彈
        playerShoot = () => {
            if(player_shot == false) {
                bullet_x = player_x;
                bullet_y = (canvas.height - PLAYER_DIST_FROM_EDGE - PLAYER_R);
                player_shot = true;
            }
        }

        // 敵人發射子彈
        enemyShoot = () => {
            // 如果已經發射,直接返回
            if(enemy_shot) {
                return;
            }

            // 根據玩家位置發射
            var col = ( player_x - enemy_distance + ENEMY_SPACING_X) / (ENEMY_R + ENEMY_SPACING_X);

            col = Math.floor(col);

            if(col < 0 || col >= ENEMY_COLS) {
                return;
            }

            //由最下方敵人發射
            for (var row = ENEMY_ROWS - 1; row >= 0; row--) {
                var enemyIndex = enemyToIndex(col, row);
                if(enemy_grid[enemyIndex] == 1) {
                    enemy_shot = true;
                    enemy_bullet_x = col * (ENEMY_R + ENEMY_SPACING_X) + enemy_distance;
                    enemy_bullet_y = row * (ENEMY_R + ENEMY_SPACING_Y) + ENEMY_R;
                    return;
                }
            }
        }

        // 移動子彈
        moveBullet = () => {
            // 玩家發射子彈
            if(player_shot) {
                bullet_y -= BULLET_SPEED;
                
                //確認子但是否擊中
                shootCheck(bullet_x, bullet_y);

                // 若子彈超出邊界,重新上膛
                if(bullet_y < 0) {
                    player_shot = false;
                }
            }

            // 敵人發射子彈
            if(enemy_shot) {
                enemy_bullet_y += BULLET_SPEED;

                enemyShootCheck();
                // 若子彈超出邊界,重新上膛
                if(enemy_bullet_y > canvas.height) {
                    enemy_shot = false;
                }
            }
        }

        // 是否擊中
        shootCheck = (x, y) => {
            var col = Math.floor ((x - enemy_distance + ENEMY_SPACING_X ) / (ENEMY_R + ENEMY_SPACING_X));
            var row = Math.floor (y / (ENEMY_R + ENEMY_SPACING_Y));

            var enemyIndex = enemyToIndex(col, row);

            if(enemy_grid[enemyIndex] == 1 && col < ENEMY_COLS && col >= 0) {
                console.log(x, y, col, row, enemy_distance, enemyIndex);
                enemy_grid[enemyIndex] = 0;
                player_shot = false;
                enemy_left -= 1;
                if(enemy_left == 0) {
                    resetGame();
                }
            }
        }

        // 敵人是否擊中
        enemyShootCheck = () => {
            var distance = Math.sqrt(Math.pow(player_x - enemy_bullet_x, 2) + Math.pow((canvas.height - PLAYER_DIST_FROM_EDGE) - enemy_bullet_y, 2));
            if(distance < PLAYER_R) {
                chance -= 1;
                enemy_shot = false;

                if(chance < 0) {
                    resetGame();
                }
            }
        }
        
        resetGame = () => {
            resetEnemy();
            chance = 3;
        }

        // 移動敵人
        moveEnemy = () => {
            if(((ENEMY_R - ENEMY_SPACING_X * 2 + enemy_distance) <= 0) || ((ENEMY_R + ENEMY_SPACING_X) * ENEMY_COLS + enemy_distance >= canvas.width)) {
                enemy_speed *= -1;
            }
            enemy_distance += enemy_speed;
        }

        // 敵人位置轉序列
        enemyToIndex = (col, row) => {
            return (col + ENEMY_COLS * row);
        } 

        // 對應序列敵人存在
        isEnemyExist = (col, row) => {
            var enemy_index = enemyToIndex(col, row);
            return (enemy_grid[enemy_index] == 1);
        }

        // 重置敵人數量
        resetEnemy = () => {
            enemy_left = 0;

            for(var row = 0; row < ENEMY_ROWS; row++) {
                for (var col = 0 ; col <ENEMY_COLS; col++) {
                    var enemy_index = enemyToIndex(col, row);
                    if (row > 1) {
                        enemy_grid[enemy_index] = 1;
                        enemy_left++;
                    } else {
                        enemy_grid[enemy_index] = 0;
                    }
                }
            }
        }

        // 顯示敵人
        showEnemy = () => {
            for(var col = 0; col < ENEMY_COLS; col++) {
                for(var row = 0; row < ENEMY_ROWS; row++) {
                    if (isEnemyExist(col, row)) {
                        var enemy_x = col * (ENEMY_R + ENEMY_SPACING_X);
                        var enemy_y = row * (ENEMY_R + ENEMY_SPACING_Y);
                        drawCircle(enemy_x + enemy_distance, enemy_y, ENEMY_R, 'red');
                    }
                }
            }
        }

        // 矩形元件
        drawRectangle = (topLeftX, topLeftY, boxWidth, boxHeight, color) => {
            canvasContext.fillStyle = color;
            canvasContext.fillRect(topLeftX, topLeftY, boxWidth, boxHeight);
        }

        // 圓形元件
        drawCircle = (centerX, centerY, r, color) => {
            canvasContext.fillStyle = color;
            canvasContext.beginPath();
            canvasContext.arc(centerX, centerY, r, 0, Math.PI * 2);
            canvasContext.fill();
        }

        // 畫線
        drawLine = (beginX, beginY, endX, endY, color) => {
            canvasContext.strokeStyle = color;
            canvasContext.beginPath();
            canvasContext.moveTo(beginX, beginY);
            canvasContext.lineTo(endX, endY);
            canvasContext.stroke();
        }

        // 畫生命
        drawLife = (lifeX, lifeY, size, color) => {
            canvasContext.fillStyle = color;
            canvasContext.beginPath();
            canvasContext.arc(lifeX + size / 4, lifeY + size / 4, size / 4, Math.PI * 1, Math.PI * 0);
            canvasContext.arc(lifeX + size * 3 / 4, lifeY + size / 4, size / 4, Math.PI * 1, Math.PI * 0);
            canvasContext.moveTo(lifeX + size, lifeY + size / 4);
            canvasContext.lineTo(lifeX + size / 2, lifeY + size);
            canvasContext.lineTo(lifeX, lifeY + size / 4);
            canvasContext.fill();
        }

        // 畫出生命
        showLife = (num) => {
            for (let life_count = 0; life_count < num; life_count++) {
                drawLife(LIFE_SIZE * life_count, 0, LIFE_SIZE, 'red');
            }
        }

        // 滑鼠點擊
        mouseClick = (event) => {
            playerShoot();
        }

        // 游標位置
        mousePos = (event) => {
            var rect = canvas.getBoundingClientRect();
            var root = document.documentElement;

            var mouse_x = event.clientX - rect.left - root.scrollLeft;

            // 玩家X軸座標
            player_x = mouse_x;
        }

    </script>
</body>

</html>

上一篇
[Day20]用Canvas打造自己的遊樂場-Galaxian 敵人學會反擊啦
下一篇
[Day22]用Canvas打造自己的遊樂場-分裂球 開工
系列文
用Canvas打造自己的遊樂場30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言